/* 
 * This file is part of Utilities for .NET (U4N)
 * Copyright (C) 2007 FutureFog (lammothmrc@yahoo.it)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using System.Windows.Forms;

namespace WowAceUpdater.u4n
{
    /// <summary>
    /// Provides static methods to perform various operations.
    /// </summary>
    public static class MiscTools
    {
        #region Methods about folders and files
        /// <summary>
        /// Removes the folder separator char (usually "\"), if present at the end of the path.
        /// </summary>
        /// <param name="path">The folder's path.</param>
        /// <returns>The folder's path without the ending separator char.</returns>
        public static string RemoveFolderSeparator(string path)
        {
            if(path == null)
                return null;

            while(path.EndsWith(Path.DirectorySeparatorChar.ToString()))
                path = path.TrimEnd(new char[] { Path.DirectorySeparatorChar });

            return path;
        }

        /// <summary>
        /// Appends the folder separator char (usually "\"), if not present at the end of the path.
        /// </summary>
        /// <param name="path">The folder's path.</param>
        /// <returns>The folder's path with the ending separator char.</returns>
        public static string AppendFolderSeparator(string path)
        {
            if(path == null)
                return null;

            return string.Concat(RemoveFolderSeparator(path), Path.DirectorySeparatorChar.ToString());
        }

        /// <summary>
        /// Tells if a file system item is a file, or is a folder, or does not exist.
        /// </summary>
        /// <param name="path">The full path to the file system item to check.</param>
        /// <returns>A value indicating if the file system item is a file, or is a folder, or does not exist.</returns>
        public static FileSystemItem CheckFileSystemItem(string path)
        {
            if((path != null) && (path != string.Empty))
            {
                try
                {
                    if(File.Exists(path))
                        return FileSystemItem.IsFile;

                    if(Directory.Exists(path))
                        return FileSystemItem.IsFolder;
                }
                catch
                { }
            }

            return FileSystemItem.DoesNotExist;
        }

        /// <summary>
        /// Creates an empty temporary file with a given extension.
        /// </summary>
        /// <param name="extension">The extension of the temporary file to create.</param>
        /// <returns>The path of the created file or null if an error has occurred.</returns>
        public static string CreateTempFileWithExtension(string extension)
        {
            if(extension == null)
                extension = string.Empty;

            if(extension != string.Empty)
                extension = string.Concat(".", extension);

            string file = null;
            FileStream stream = null;

            try
            {
                do
                {
                    file = string.Concat(AppendFolderSeparator(Path.GetTempPath()), Guid.NewGuid().ToString(), extension);
                }
                while(File.Exists(file));

                stream = File.Create(file);
                return file;
            }
            catch
            {
                return null;
            }
            finally
            {
                if(stream != null)
                {
                    stream.Close();
                    stream.Dispose();
                }
            }
        }

        /// <summary>
        /// Grant or revoke access to a network path (wrapping the NET USE command). 
        /// </summary>
        /// <param name="netPath">The network path whose access must be granted or revoked.</param>
        /// <param name="login">The login to access the network path or null to revoke access.</param>
        /// <param name="password">The password to access the network path or null to revoke access.</param>
        /// <returns>True or false for success or failure.</returns>
        public static bool NetworkPathLogin(string netPath, string login, string password)
        {
            if(string.IsNullOrEmpty(netPath))
                return false;

            Process process = null;
            try
            {
                process = new Process();
                process.StartInfo.FileName = "NET.EXE";
                process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
                process.StartInfo.CreateNoWindow = true;
                process.StartInfo.ErrorDialog = false;

                if((login == null) || (password == null))
                    process.StartInfo.Arguments = string.Format("USE {0} /DELETE /YES", netPath);
                else
                    process.StartInfo.Arguments = string.Format("USE {0} {1} /USER:{2} /PERSISTENT:YES /YES", netPath, password, login);

                process.Start();
                process.WaitForExit();
                return (process.ExitCode == 0);
            }
            catch
            {
                return false;
            }
            finally
            {
                if(process != null)
                {
                    try
                    {
                        process.Close();
                        process.Dispose();
                    }
                    catch
                    { }
                }
            }
        }

        /// <summary>
        /// Remove from a path all the invalid chars.
        /// </summary>
        /// <param name="pathToEdit">The input path to edit.</param>
        /// <param name="isFolder">Tells if to keep the path and volume separators.</param>
        /// <returns>The path without the invalid chars.</returns>
        public static string RemoveInvalidPathChars(string pathToEdit, bool isFolder)
        {
            if(!string.IsNullOrEmpty(pathToEdit))
            {
                List<char> invalidChars = new List<char>(Path.GetInvalidFileNameChars());

                foreach(char chr in Path.GetInvalidPathChars())
                {
                    if(!invalidChars.Contains(chr))
                        invalidChars.Add(chr);
                }

                if(isFolder)
                {
                    if(invalidChars.Contains(Path.DirectorySeparatorChar))
                        invalidChars.Remove(Path.DirectorySeparatorChar);

                    if(invalidChars.Contains(Path.VolumeSeparatorChar))
                        invalidChars.Remove(Path.VolumeSeparatorChar);
                }
                else
                {
                    if(!invalidChars.Contains(Path.DirectorySeparatorChar))
                        invalidChars.Add(Path.DirectorySeparatorChar);

                    if(!invalidChars.Contains(Path.VolumeSeparatorChar))
                        invalidChars.Add(Path.VolumeSeparatorChar);
                }

                char[] searchChars = invalidChars.ToArray();
                int index;

                while((index = pathToEdit.IndexOfAny(searchChars)) >= 0)
                    pathToEdit = pathToEdit.Remove(index, 1);
            }

            return pathToEdit;
        }
        #endregion

        #region Methods about assembly paths
        /// <summary>
        /// Returns the folder where the application entry assembly is located in.
        /// </summary>
        /// <param name="withFolderSeparator">True or false to append or remove the ending separator char.</param>
        /// <returns>The folder where the application entry assembly is located in.</returns>
        public static string GetApplicationFolder(bool withFolderSeparator)
        {
            return GetAssemblyFolder(Assembly.GetEntryAssembly(), withFolderSeparator);
        }

        /// <summary>
        /// Returns the folder where the Utilities's assembly is located in.
        /// </summary>
        /// <param name="withFolderSeparator">True or false to append or remove the ending separator char.</param>
        /// <returns>The folder where the Utilities's assembly is located in.</returns>
        public static string GetUtilitiesFolder(bool withFolderSeparator)
        {
            return GetAssemblyFolder(Assembly.GetExecutingAssembly(), withFolderSeparator);
        }

        /// <summary>
        /// Returns the folder where a given assembly is located in.
        /// </summary>
        /// <param name="assembly">The assembly whose folder will be retrieved.</param>
        /// <param name="withFolderSeparator">True or false to append or remove the ending separator char.</param>
        /// <returns>The folder where the given assembly is located in.</returns>
        public static string GetAssemblyFolder(Assembly assembly, bool withFolderSeparator)
        {
            string path = Path.GetDirectoryName(assembly.Location);

            if(withFolderSeparator)
                return AppendFolderSeparator(path);
            else
                return RemoveFolderSeparator(path);
        }

        /// <summary>
        /// Returns the full path of the application entry assembly.
        /// </summary>
        /// <param name="withExtension">True or false to leave or remove the extension.</param>
        /// <returns>The full path of the application entry assembly.</returns>
        public static string GetApplicationFullPath(bool withExtension)
        {
            return GetAssemblyFullPath(Assembly.GetEntryAssembly(), withExtension);
        }

        /// <summary>
        /// Returns the full path of the Utilities's assembly.
        /// </summary>
        /// <param name="withExtension">True or false to leave or remove the extension.</param>
        /// <returns>The full path of the Utilities's assembly.</returns>
        public static string GetUtilitiesFullPath(bool withExtension)
        {
            return GetAssemblyFullPath(Assembly.GetExecutingAssembly(), withExtension);
        }

        /// <summary>
        /// Returns the full path of a given assembly.
        /// </summary>
        /// <param name="assembly">The assembly whose full path will be retrieved.</param>
        /// <param name="withExtension">True or false to leave or remove the extension.</param>
        /// <returns>The full path of the given assembly.</returns>
        public static string GetAssemblyFullPath(Assembly assembly, bool withExtension)
        {
            if(withExtension)
                return assembly.Location;
            else
                return string.Concat(GetAssemblyFolder(assembly, true), Path.GetFileNameWithoutExtension(assembly.Location));
        }
        #endregion

        #region Methods about path formatting
        [DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
        private static extern int GetLongPathName(string lpszShortPath, StringBuilder lpszLongPath, int cchBuffer);

        [DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
        private static extern int GetShortPathName(string lpszLongPath, StringBuilder lpszShortPath, int cchBuffer);

        /// <summary>
        /// Converts the specified path to its long form ("C:\Program Files\Common Files").
        /// </summary>
        /// <param name="shortPath">The path to convert.</param>
        /// <returns>The result of the conversion.</returns>
        public static string ConvertPathToLong(string shortPath)
        {
            if((shortPath == null) || (shortPath == string.Empty))
                return shortPath;

            StringBuilder buffer = new StringBuilder(short.MaxValue);
            return (GetLongPathName(shortPath, buffer, buffer.Capacity) == 0) ? shortPath : buffer.ToString();
        }

        /// <summary>
        /// Converts the specified path to its short form ("C:\PROGRA~1\COMMON~1").
        /// </summary>
        /// <param name="longPath">The path to convert.</param>
        /// <returns>The result of the conversion.</returns>
        public static string ConvertPathToShort(string longPath)
        {
            if((longPath == null) || (longPath == string.Empty))
                return longPath;

            StringBuilder buffer = new StringBuilder(short.MaxValue);
            return (GetShortPathName(longPath, buffer, buffer.Capacity) == 0) ? longPath : buffer.ToString();
        }
        #endregion

        #region Methods about text cryptography
        /// <summary>
        /// Encrypts a given string.
        /// </summary>
        /// <param name="text">The string to encrypt.</param>
        /// <returns>The encrypted string or null if an error has occurred.</returns>
        public static string EncryptText(string text)
        {
            return EncryptText(text, GetRijndaelManaged());
        }

        /// <summary>
        /// Encrypts a given string.
        /// </summary>
        /// <param name="text">The string to encrypt.</param>
        /// <param name="customKey">The custom key (max 32 chars) used to encrypt the string.</param>
        /// <returns>The encrypted string or null if an error has occurred.</returns>
        public static string EncryptText(string text, string customKey)
        {
            return EncryptText(text, GetRijndaelManaged(customKey));
        }

        /// <summary>
        /// Encrypts a given string.
        /// </summary>
        /// <param name="text">The string to encrypt.</param>
        /// <param name="customRijndaelManaged">The custom RijndaelManaged used to encrypt the string.</param>
        /// <returns>The encrypted string or null if an error has occurred.</returns>
        public static string EncryptText(string text, RijndaelManaged customRijndaelManaged)
        {
            if((text == null) || (customRijndaelManaged == null))
                return null;

            try
            {
                byte[] inbuf = Encoding.UTF8.GetBytes(text);
                byte[] outbuf = customRijndaelManaged.CreateEncryptor().TransformFinalBlock(inbuf, 0, inbuf.Length);
                return Convert.ToBase64String(outbuf);
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// Decrypts a given string.
        /// </summary>
        /// <param name="text">The string to decrypt.</param>
        /// <returns>The decrypted string or null if an error has occurred.</returns>
        public static string DecryptText(string text)
        {
            return DecryptText(text, GetRijndaelManaged());
        }

        /// <summary>
        /// Decrypts a given string.
        /// </summary>
        /// <param name="text">The string to decrypt.</param>
        /// <param name="customKey">The custom key (max 32 chars) used to decrypt the string.</param>
        /// <returns>The decrypted string or null if an error has occurred.</returns>
        public static string DecryptText(string text, string customKey)
        {
            return DecryptText(text, GetRijndaelManaged(customKey));
        }

        /// <summary>
        /// Decrypts a given string.
        /// </summary>
        /// <param name="text">The string to decrypt.</param>
        /// <param name="customRijndaelManaged">The custom RijndaelManaged used to decrypt the string.</param>
        /// <returns>The decrypted string or null if an error has occurred.</returns>
        public static string DecryptText(string text, RijndaelManaged customRijndaelManaged)
        {
            if((text == null) || (text == string.Empty) || (customRijndaelManaged == null))
                return null;

            try
            {
                byte[] inbuf = Convert.FromBase64String(text);
                byte[] outbuf = customRijndaelManaged.CreateDecryptor().TransformFinalBlock(inbuf, 0, inbuf.Length);
                return Encoding.UTF8.GetString(outbuf);
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// Returns the RijndaelManaged used to encrypt and decrypt text.
        /// </summary>
        /// <returns>The RijndaelManaged used to encrypt and decrypt text.</returns>
        private static RijndaelManaged GetRijndaelManaged()
        {
            RijndaelManaged rij = new RijndaelManaged();
            rij.Key = new byte[] { 0x81, 0xCE, 0xD9, 0x27, 0xFE, 0x48, 0xAE, 0xCE, 0x6A, 0xFA, 0x4F, 0x39, 0x77, 0xD7, 0x2E, 0x89, 0x4F, 0x0C, 0x13, 0x5F, 0x95, 0x79, 0x35, 0xBE, 0x94, 0xF7, 0xDD, 0x79, 0x94, 0xA9, 0xA1, 0x60 };
            rij.IV = new byte[] { 0xC8, 0x1C, 0x46, 0x74, 0xD8, 0x92, 0x03, 0x01, 0xCB, 0xFD, 0x16, 0xD8, 0x64, 0x51, 0xC5, 0x97 };
            return rij;
        }

        /// <summary>
        /// Returns the RijndaelManaged used to encrypt and decrypt text.
        /// </summary>
        /// <param name="customKey">The custom key used to create the RijndaelManaged.</param>
        /// <returns>The RijndaelManaged used to encrypt and decrypt text.</returns>
        private static RijndaelManaged GetRijndaelManaged(string customKey)
        {
            if((customKey == null) || (customKey == string.Empty))
                return null;

            if(customKey.Length < 32)
                customKey = string.Concat(customKey, new string(' ', 32 - customKey.Length));

            try
            {
                RijndaelManaged rij = new RijndaelManaged();
                rij.Key = Encoding.UTF8.GetBytes(customKey.Substring(0, 32));
                rij.IV = Encoding.UTF8.GetBytes(customKey.Substring(0, 16));
                return rij;
            }
            catch
            {
                return null;
            }
        }
        #endregion

        #region Methods about text escaping
        /// <summary>
        /// Converts a string to the escape format (%xx%yy...).
        /// </summary>
        /// <param name="text">The string to escape.</param>
        /// <returns>The escaped string or null if an error has occurred.</returns>
        public static string EscapeText(string text)
        {
            return EscapeText(text, false);
        }

        /// <summary>
        /// Converts a string to the escape format (%xx%yy...).
        /// </summary>
        /// <param name="text">The string to escape.</param>
        /// <param name="onlySymbols">True to only escape symbols or false to also escape alphanumeric chars.</param>
        /// <returns>The escaped string or null if an error has occurred.</returns>
        public static string EscapeText(string text, bool onlySymbols)
        {
            if((text == null) || (text == string.Empty))
                return string.Empty;

            string excluded = string.Empty;
            if(onlySymbols)
                excluded = "1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM";

            try
            {
                char[] chars = text.ToCharArray();
                string[] retval = new string[chars.Length];

                for(int i = 0; i < chars.Length; i++)
                {
                    if(onlySymbols && (excluded.IndexOf(chars[i]) >= 0))
                        retval[i] = chars[i].ToString();
                    else
                        retval[i] = Uri.HexEscape(chars[i]);
                }

                return string.Concat(retval);
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// Converts an escaped string (%xx%yy...) to the standard representation.
        /// </summary>
        /// <param name="text">The string to unescape.</param>
        /// <returns>The unescaped string or null if an error has occurred.</returns>
        public static string UnescapeText(string text)
        {
            if((text == null) || (text == string.Empty))
                return string.Empty;

            try
            {
                string[] retval = new string[text.Length];

                for(int i = 0; i < text.Length; )
                {
                    if(Uri.IsHexEncoding(text, i))
                        retval[i] = Uri.HexUnescape(text, ref i).ToString();
                    else
                        retval[i] = text.Substring(i++, 1);
                }

                return string.Concat(retval);
            }
            catch
            {
                return null;
            }
        }
        #endregion

        #region Methods about the 7-bit GSM alphabet
        /// <summary>
        /// Converts an unicode string GSM to its 7-bit GSM alphabet representation.
        /// </summary>
        /// <param name="unicode">The unicode string to convert.</param>
        /// <returns>The converted 7-bit GSM alphabet string.</returns>
        public static string StringTo7BitGsm(string unicode)
        {
            StringBuilder builder = new StringBuilder();
            char append;
            for(int i = 0; i < unicode.Length; i++)
            {
                append = unicode[i];
                for(int j = 0; j < _7BitGsmChars.Length; j++)
                {
                    if(_7BitGsmChars[j, 0] == (int)append)
                    {
                        append = (char)_7BitGsmChars[j, 1];
                        break;
                    }
                }
                builder.Append(append);
            }
            return builder.ToString();
        }

        /// <summary>
        /// Converts a 7-bit GSM alphabet string to its unicode representation.
        /// </summary>
        /// <param name="gsm">The 7-bit GSM alphabet string to convert.</param>
        /// <returns>The converted unicode string.</returns>
        public static string StringFrom7BitGsm(string gsm)
        {
            StringBuilder builder = new StringBuilder();
            char append;
            for(int i = 0; i < gsm.Length; i++)
            {
                append = gsm[i];
                for(int j = 0; j < _7BitGsmChars.Length; j++)
                {
                    if(_7BitGsmChars[j, 1] == (int)append)
                    {
                        append = (char)_7BitGsmChars[j, 0];
                        break;
                    }
                }
                builder.Append(append);
            }
            return builder.ToString();
        }

        /// <summary>
        /// Matrix containing unicode chars with the relative 7-bit GSM chars.
        /// </summary>
        private static int[,] _7BitGsmChars = {
                                                  {0x0040, 0x0000},
                                                  {0x00A3, 0x0001},
                                                  {0x0024, 0x0002},
                                                  {0x00A5, 0x0003},
                                                  {0x00E8, 0x0004},
                                                  {0x00E9, 0x0005},
                                                  {0x00F9, 0x0006},
                                                  {0x00EC, 0x0007},
                                                  {0x00F2, 0x0008},
                                                  {0x00E7, 0x0009},
                                                  {0x000A, 0x000A},
                                                  {0x00D8, 0x000B},
                                                  {0x00F8, 0x000C},
                                                  {0x000D, 0x000D},
                                                  {0x00C5, 0x000E},
                                                  {0x00E5, 0x000F},
                                                  {0x0394, 0x0010},
                                                  {0x005F, 0x0011},
                                                  {0x03A6, 0x0012},
                                                  {0x0393, 0x0013},
                                                  {0x039B, 0x0014},
                                                  {0x03A9, 0x0015},
                                                  {0x03A0, 0x0016},
                                                  {0x03A8, 0x0017},
                                                  {0x03A3, 0x0018},
                                                  {0x0398, 0x0019},
                                                  {0x039E, 0x001A},
                                                  {0x00A0, 0x001B},
                                                  {0x000C, 0x1B0A},
                                                  {0x005E, 0x1B14},
                                                  {0x007B, 0x1B28},
                                                  {0x007D, 0x1B29},
                                                  {0x005C, 0x1B2F},
                                                  {0x005B, 0x1B3C},
                                                  {0x007E, 0x1B3D},
                                                  {0x005D, 0x1B3E},
                                                  {0x007C, 0x1B40},
                                                  {0x20AC, 0x1B65},
                                                  {0x00C6, 0x001C},
                                                  {0x00E6, 0x001D},
                                                  {0x00DF, 0x001E},
                                                  {0x00C9, 0x001F},
                                                  {0x0020, 0x0020},
                                                  {0x0021, 0x0021},
                                                  {0x0022, 0x0022},
                                                  {0x0023, 0x0023},
                                                  {0x00A4, 0x0024},
                                                  {0x0025, 0x0025},
                                                  {0x0026, 0x0026},
                                                  {0x0027, 0x0027},
                                                  {0x0028, 0x0028},
                                                  {0x0029, 0x0029},
                                                  {0x002A, 0x002A},
                                                  {0x002B, 0x002B},
                                                  {0x002C, 0x002C},
                                                  {0x002D, 0x002D},
                                                  {0x002E, 0x002E},
                                                  {0x002F, 0x002F},
                                                  {0x0030, 0x0030},
                                                  {0x0031, 0x0031},
                                                  {0x0032, 0x0032},
                                                  {0x0033, 0x0033},
                                                  {0x0034, 0x0034},
                                                  {0x0035, 0x0035},
                                                  {0x0036, 0x0036},
                                                  {0x0037, 0x0037},
                                                  {0x0038, 0x0038},
                                                  {0x0039, 0x0039},
                                                  {0x003A, 0x003A},
                                                  {0x003B, 0x003B},
                                                  {0x003C, 0x003C},
                                                  {0x003D, 0x003D},
                                                  {0x003E, 0x003E},
                                                  {0x003F, 0x003F},
                                                  {0x00A1, 0x0040},
                                                  {0x0041, 0x0041},
                                                  {0x0042, 0x0042},
                                                  {0x0043, 0x0043},
                                                  {0x0044, 0x0044},
                                                  {0x0045, 0x0045},
                                                  {0x0046, 0x0046},
                                                  {0x0047, 0x0047},
                                                  {0x0048, 0x0048},
                                                  {0x0049, 0x0049},
                                                  {0x004A, 0x004A},
                                                  {0x004B, 0x004B},
                                                  {0x004C, 0x004C},
                                                  {0x004D, 0x004D},
                                                  {0x004E, 0x004E},
                                                  {0x004F, 0x004F},
                                                  {0x0050, 0x0050},
                                                  {0x0051, 0x0051},
                                                  {0x0052, 0x0052},
                                                  {0x0053, 0x0053},
                                                  {0x0054, 0x0054},
                                                  {0x0055, 0x0055},
                                                  {0x0056, 0x0056},
                                                  {0x0057, 0x0057},
                                                  {0x0058, 0x0058},
                                                  {0x0059, 0x0059},
                                                  {0x005A, 0x005A},
                                                  {0x00C4, 0x005B},
                                                  {0x00D6, 0x005C},
                                                  {0x00D1, 0x005D},
                                                  {0x00DC, 0x005E},
                                                  {0x00A7, 0x005F},
                                                  {0x00BF, 0x0060},
                                                  {0x0061, 0x0061},
                                                  {0x0062, 0x0062},
                                                  {0x0063, 0x0063},
                                                  {0x0064, 0x0064},
                                                  {0x0065, 0x0065},
                                                  {0x0066, 0x0066},
                                                  {0x0067, 0x0067},
                                                  {0x0068, 0x0068},
                                                  {0x0069, 0x0069},
                                                  {0x006A, 0x006A},
                                                  {0x006B, 0x006B},
                                                  {0x006C, 0x006C},
                                                  {0x006D, 0x006D},
                                                  {0x006E, 0x006E},
                                                  {0x006F, 0x006F},
                                                  {0x0070, 0x0070},
                                                  {0x0071, 0x0071},
                                                  {0x0072, 0x0072},
                                                  {0x0073, 0x0073},
                                                  {0x0074, 0x0074},
                                                  {0x0075, 0x0075},
                                                  {0x0076, 0x0076},
                                                  {0x0077, 0x0077},
                                                  {0x0078, 0x0078},
                                                  {0x0079, 0x0079},
                                                  {0x007A, 0x007A},
                                                  {0x00E4, 0x007B},
                                                  {0x00F6, 0x007C},
                                                  {0x00F1, 0x007D},
                                                  {0x00FC, 0x007E},
                                                  {0x00E0, 0x007F}
                                              };
        #endregion

        #region Properties about the operating system
        [DllImport("Uxtheme.dll")]
        private static extern bool IsThemeActive();

        /// <summary>
        /// Gets a value indicating if the operating system is a Windows 2000 or a newer one.
        /// </summary>
        public static bool IsWindows2000OrNewer
        {
            get { return (Environment.OSVersion.Platform == PlatformID.Win32NT) && (Environment.OSVersion.Version.Major >= 5); }
        }

        /// <summary>
        /// Gets a value indicating if the operating system is a Windows XP or a newer one.
        /// </summary>
        public static bool IsWindowsXpOrNewer
        {
            get
            {
                return
                    (Environment.OSVersion.Platform == PlatformID.Win32NT) &&
                    (
                        (Environment.OSVersion.Version.Major >= 6) ||
                        (
                            (Environment.OSVersion.Version.Major == 5) &&
                            (Environment.OSVersion.Version.Minor >= 1)
                        )
                    );
            }
        }

        /// <summary>
        /// Gets a value indicating if the operating system is a Windows Vista or a newer one.
        /// </summary>
        public static bool IsWindowsVistaOrNewer
        {
            get { return (Environment.OSVersion.Platform == PlatformID.Win32NT) && (Environment.OSVersion.Version.Major >= 6); }
        }

        /// <summary>
        /// Gets a value indicating if the operating system has visual styles enabled.
        /// </summary>
        public static bool IsWindowsThemed
        {
            get
            {
                if(!IsWindowsXpOrNewer)
                    return false;

                try
                {
                    return IsThemeActive();
                }
                catch
                {
                    return false;
                }
            }
        }

        /// <summary>
        /// Gets a value indicating if the application has visual styles enabled.
        /// </summary>
        public static bool IsApplicationThemed
        {
            get
            {
                if(!IsWindowsXpOrNewer)
                    return false;

                try
                {
                    return IsThemeActive() && Application.RenderWithVisualStyles;
                }
                catch
                {
                    return false;
                }
            }
        }

        /// <summary>
        /// Gets a value indicating if the operating system can draw icons using alpha blend.
        /// </summary>
        public static bool IsAlphaBlendActive
        {
            get
            {
                if(!IsWindowsXpOrNewer)
                    return false;

                Icon icoTest = null;
                Bitmap bmpTest = null;

                try
                {
                    icoTest = new Icon(typeof(ResourcesBase), "AlphaIconTest.ico");
                    bmpTest = icoTest.ToBitmap();

                    if(Bitmap.GetPixelFormatSize(bmpTest.PixelFormat) < 32)
                        return false;

                    BitmapData bmpData = bmpTest.LockBits(new Rectangle(Point.Empty, bmpTest.Size), ImageLockMode.ReadOnly, bmpTest.PixelFormat);
                    Color pixelColor = Color.FromArgb(Marshal.ReadInt32(bmpData.Scan0, 0));
                    if((pixelColor.A > 0) && (pixelColor.A < 255))
                    {
                        bmpTest.UnlockBits(bmpData);
                        return true;
                    }

                    bmpTest.UnlockBits(bmpData);
                    return false;
                }
                catch
                {
                    return false;
                }
                finally
                {
                    if(icoTest != null)
                        icoTest.Dispose();

                    if(bmpTest != null)
                        bmpTest.Dispose();
                }
            }
        }
        #endregion

        #region Methods for moving mouse cursor
        [DllImport("User32.dll", SetLastError = true)]
        private static extern int SetCursorPos(int x, int y);

        [DllImport("User32.dll", SetLastError = true)]
        private static extern int ClientToScreen(IntPtr hHandle, ref Win32Point point);

        [DllImport("User32.dll")]
        private static extern IntPtr GetDesktopWindow();

        [StructLayout(LayoutKind.Sequential)]
        private struct Win32Point
        {
            public int X;
            public int Y;
        }

        /// <summary>
        /// Moves the mouse cursor to a new position (in screen coordinates).
        /// </summary>
        /// <param name="position">The new position of the cursor.</param>
        /// <exception cref="Exception">A generic exception has occurred.</exception>
        /// <exception cref="Win32Exception">An exception has occurred while calling the Windows API.</exception>
        public static void SetCursorPosition(Point position)
        {
            SetCursorPosition(position.X, position.Y);
        }

        /// <summary>
        /// Moves the mouse cursor to a new position (in screen coordinates).
        /// </summary>
        /// <param name="x">The new x-value of the cursor.</param>
        /// <param name="y">The new y-value of the cursor.</param>
        /// <exception cref="Exception">A generic exception has occurred.</exception>
        /// <exception cref="Win32Exception">An exception has occurred while calling the Windows API.</exception>
        public static void SetCursorPosition(int x, int y)
        {
            try
            {
                Win32Point point = new Win32Point();
                point.X = x;
                point.Y = y;

                if(ClientToScreen(GetDesktopWindow(), ref point) == 0)
                    throw new Win32Exception(Marshal.GetLastWin32Error());

                if(SetCursorPos(point.X, point.Y) == 0)
                    throw new Win32Exception(Marshal.GetLastWin32Error());
            }
            catch
            {
                throw;
            }
        }

        #endregion

        #region Methods about the close button
        [DllImport("User32.dll")]
        private static extern IntPtr GetSystemMenu(IntPtr hForm, bool bRevert);

        [DllImport("User32.dll", SetLastError = true)]
        private static extern int DrawMenuBar(IntPtr hForm);

        [DllImport("User32.dll", SetLastError = true)]
        private static extern int EnableMenuItem(IntPtr hMenu, uint iItem, uint iEnable);

        private const uint SC_CLOSE = 0xF060;
        private const uint MF_BYCOMMAND = 0x0000;
        private const uint MF_ENABLED = 0x0000;
        private const uint MF_GRAYED = 0x0001;

        /// <summary>
        /// Enables or disables the close button (and its relative system menu) of a window.
        /// </summary>
        /// <param name="window">The window containing the button.</param>
        /// <param name="enable">True to enable the close button, false otherwise.</param>
        /// <exception cref="Exception">A generic exception has occurred.</exception>
        /// <exception cref="Win32Exception">An exception has occurred while calling the Windows API.</exception>
        public static void SetCloseButtonStatus(IWin32Window window, bool enable)
        {
            try
            {
                IntPtr hMenu = GetSystemMenu(window.Handle, false);
                if(hMenu == IntPtr.Zero)
                    throw new Exception("Cannot retrieve the system menu.");

                if(EnableMenuItem(hMenu, SC_CLOSE, MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED)) == -1)
                    throw new Win32Exception(Marshal.GetLastWin32Error());

                if(DrawMenuBar(window.Handle) == 0)
                    throw new Win32Exception(Marshal.GetLastWin32Error());
            }
            catch
            {
                throw;
            }
        }
        #endregion

#if false
        #region Displays the About Screen
        /// <summary>
        /// Displays the Utilities' About Screen.
        /// </summary>
        public static void AboutUtilities()
        {
            AboutUtilities(false);
        }

        /// <summary>
        /// Displays the Utilities' About Screen.
        /// </summary>
        /// <param name="showInTaskbar">Determines whether the form is displayed in the Windows taskbar.</param>
        public static void AboutUtilities(bool showInTaskbar)
        {
            using (Form form = new FrmAboutUtilities(showInTaskbar))
                form.ShowDialog();
        }
                #endregion 
#endif

        #region Internal methods
        /// <summary>
        /// If a string is null returns an empty string, otherwise returns the string itself.
        /// </summary>
        /// <param name="value">The string to check.</param>
        /// <returns>If the string is null returns an empty string, otherwise returns the string itself.</returns>
        internal static string NullToEmpty(string value)
        {
            return (value == null) ? string.Empty : value;
        }

        /// <summary>
        /// Frees allocated memory identified by a pointer.
        /// </summary>
        /// <param name="hPointer">The pointer to the memory area to free.</param>
        internal static void FreeMemory(IntPtr hPointer)
        {
            if(hPointer != IntPtr.Zero)
            {
                try
                {
                    Marshal.FreeHGlobal(hPointer);
                }
                catch
                { }
            }
        }

        /// <summary>
        /// Returns the name of the Utilities assembly to use in serializations/deserializations.
        /// </summary>
        /// <returns>The name of the Utilities assembly.</returns>
        internal static string GetUtilitiesAssemblyName()
        {
            return Assembly.GetExecutingAssembly().GetName().Name;
        }
        #endregion
    }

    /// <summary>
    /// Represents the identifier of an image inside an ImageList.
    /// </summary>
    internal class ImageId
    {
        #region Public fields
        /// <summary>
        /// Represents the default index of the image inside an ImageList.
        /// </summary>
        public static readonly int DefaultImageIndex = -1;

        /// <summary>
        /// Represents the default key accessor of the image inside an ImageList.
        /// </summary>
        public static readonly string DefaultImageKey = string.Empty;
        #endregion

        #region Class constructors
        private int _ImageIndex = DefaultImageIndex;
        private string _ImageKey = DefaultImageKey;

        /// <summary>
        /// Creates a new instance of the class.
        /// </summary>
        public ImageId()
        { }

        /// <summary>
        /// Creates a new instance of the class.
        /// </summary>
        /// <param name="imageIndex">The index of the image inside an ImageList.</param>
        public ImageId(int imageIndex)
        {
            ImageIndex = imageIndex;
        }

        /// <summary>
        /// Creates a new instance of the class.
        /// </summary>
        /// <param name="imageKey">The key accessor of the image inside an ImageList.</param>
        public ImageId(string imageKey)
        {
            ImageKey = imageKey;
        }
        #endregion

        #region Public properties
        /// <summary>
        /// Gets or sets the index of the image inside an ImageList.
        /// </summary>
        public int ImageIndex
        {
            get { return _ImageIndex; }
            set
            {
                _ImageIndex = (value < 0) ? DefaultImageIndex : value;
                _ImageKey = DefaultImageKey;
            }
        }

        /// <summary>
        /// Gets or sets the key accessor of the image inside an ImageList.
        /// </summary>
        public string ImageKey
        {
            get { return _ImageKey; }
            set
            {
                _ImageKey = (value == null) ? DefaultImageKey : value;
                _ImageIndex = DefaultImageIndex;
            }
        }
        #endregion

        #region Public methods
        /// <summary>
        /// Returns the image identified by this instance into an ImageList.
        /// </summary>
        /// <param name="imageList">The ImageList to search into.</param>
        /// <returns>The image identified by this instance into the ImageList or null.</returns>
        public Image GetImage(ImageList imageList)
        {
            if(imageList == null)
                return null;

            return ((_ImageIndex >= 0) && (_ImageIndex < imageList.Images.Count)) ? imageList.Images[_ImageIndex] : imageList.Images[_ImageKey];
        }
        #endregion
    }

    /// <summary>
    /// Represents the type of a file system item.
    /// </summary>
    public enum FileSystemItem
    {
        /// <summary>
        /// The file system item is a file.
        /// </summary>
        IsFile,

        /// <summary>
        /// The file system item is a folder.
        /// </summary>
        IsFolder,

        /// <summary>
        /// The file system item does not exist.
        /// </summary>
        DoesNotExist
    }
}